﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using log4net;

namespace WebUtils
{
    public static class Utils
    {
        static readonly ILog log = LogManager.GetLogger(typeof(Utils));

        public static IEnumerable<TResult> ForEach<T, TResult>(
            this IEnumerable<T> pList, 
            Func<T, TResult> pFunc)
        {
            if (pList != null)
                foreach (var t in pList)
                    yield return pFunc(t);
            yield break;
        }

        public static void ForEach<T>(
            this IEnumerable<T> pList, 
            Action<T> pAction)
        {
            if (pList != null)
                foreach (var t in pList)
                    pAction(t);
        }

        public static string CallWeb(this IWebRequest pRequest) 
        { 
            return pRequest.CallWeb(null);
        }

        public static string BuildURLEncodedParameter(
            this IEnumerable<UnEncodedURLParameter> pParameters, 
            bool pPrependQuestionMark)
        {
            var result = new StringBuilder();
            bool isFirst = true;
            pParameters
                .EncodeParameters()
                .ForEach(param =>
                {
                    if (isFirst)
                    {
                        if (pPrependQuestionMark)
                            result.Append("?");
                    }
                    else
                    {
                        result.Append("&");
                    }

                    isFirst = false;
                    result.AppendFormat("{0}={1}", param.Key, param.Value);
                });
            return result.ToString();
        }

        /// <summary></summary>
        /// <param name="pRequest"></param>
        /// <param name="pAdditionalSetupAction">
        /// The last code to be run on the actual request object before it is fired off. May be null.
        /// </param>
        /// <returns>The raw response in UTF8</returns>
        public static string CallWeb(
            this IWebRequest pRequest, 
            Action<HttpWebRequest> pAdditionalSetupAction)
        {
            // create the request url
            var requestUrl = new StringBuilder();
            requestUrl.Append(pRequest.Address);
            requestUrl.Append(BuildURLEncodedParameter(pRequest.GetParameters, true));

            var request = (HttpWebRequest) HttpWebRequest.Create(requestUrl.ToString());
            if (log.IsDebugEnabled)
            {
                log.DebugFormat("Request: {0}", request);
            }

            // add the headers
            pRequest
                .Headers
                .ForEach(header =>
                    {
                        if (log.IsDebugEnabled)
                            log.DebugFormat("Header[Value = \"{0}\", Value=\"{1}\"]", header.Key, header.Value);
                        return header;
                    })
                .ForEach(header =>
                    {
                        var headerName = header.Key.ToUpper();
                        if (headerName.Equals("CONTENT-TYPE"))
                            request.ContentType = header.Value;
                        else
                            request.Headers.Add(header.Key, header.Value);
                    });

            // set the other properties
            var verb = pRequest.Verb.ToString();
            if (log.IsDebugEnabled)
                log.DebugFormat("Verb: {0}", verb);
            request.Method = verb;

            request.PreAuthenticate = true;
#if PocketPC
#else
            request.Proxy = WebRequest.DefaultWebProxy;
#endif

            // do post processing on the request before it is fired off
            if (pAdditionalSetupAction != null)
            {
                if (log.IsDebugEnabled)
                    log.DebugFormat("Calling post request processing...");
                pAdditionalSetupAction(request);
                if (log.IsDebugEnabled)
                    log.DebugFormat("Post processing complete");
            }

            // return the response in string format
            return GetWebResponse(request);
        }

        static string GetWebResponse(HttpWebRequest request)
        {
            Func<Stream, string> responseReader = str =>
            {
                using (StreamReader reader = new StreamReader(str, Encoding.UTF8))
                    return reader.ReadToEnd();
            };

            try
            {
                using (var response       = (HttpWebResponse) request.GetResponse())
                using (var responseStream = response.GetResponseStream())
                {
                    if (log.IsDebugEnabled)
                        log.DebugFormat("Trying to read request's repsonse...");
                    var data = responseReader(responseStream);
                    if (log.IsDebugEnabled)
                        log.Debug(
                              "Response:"
                            + Environment.NewLine
                            + data);
                    return data;
                }
            }

            catch (WebException ex)
            {
                if (log.IsErrorEnabled)
                {
                    using (var response       = ex.Response)
                    using (var responseStream = response.GetResponseStream())
                    {
                        log.Error(
                              "Uncaught Exception:"
                            + ex.Message);
                        log.Error(
                              "Full Response From Server:"
                            + Environment.NewLine
                            + responseReader(responseStream));
                    }
                }

                throw ex;
            }
        }
    }
}
